package figuras;

import java.util.ArrayList;

import javax.media.opengl.GL;

public class BSplines {

	private ArrayList<PuntoVector> puntosControl;
	private ArrayList<PuntoVector> perfil;
	private int nPasos;

	public BSplines() {
		nPasos = 50;
		puntosControl = new ArrayList<PuntoVector>();
		perfil=null;
	}

	public ArrayList<PuntoVector> aproxima(int tin, int tout, int nPasos) {
		ArrayList<PuntoVector> lista = new ArrayList<PuntoVector>();
		if (tin < tout) {
			this.nPasos = nPasos;
			PuntoVector p1 = obtenP(tin);
			lista.add(p1);
			double ti;
			for (int i = 1; i <= nPasos; i++) {
				ti = tin + (i * ((tout - tin) / (double) nPasos));
				PuntoVector p2 = obtenP(ti);
				lista.add(i, p2);
			}
		}
		return lista;
	}

	public PuntoVector obtenP(double t) {
		float px = 0;
		float py = 0;
		for (int i = 0; i < puntosControl.size(); i++) {
			PuntoVector p = puntosControl.get(i);
			px += p.getX() * funcionN(t - i);
			py += p.getY() * funcionN(t - i);
		}
		return new PuntoVector(px, py, 0.0f, 1);
	}

	private double funcionN(double t) {
		double n = 0;
		if ((t >= 0) && (t <= 1))
			n = 0.5 * Math.pow(t, 2.0);
		else if ((t > 1) && (t <= 2))
			n = 0.75 - Math.pow(t - 1.5, 2.0);
		else if ((t > 2) && (t <= 3))
			n = 0.5 * Math.pow(3 - t, 2.0);
		return n;
	}

	public void pintaConPuntos(GL gl) {
		this.pintaPuntosControl(gl);
		gl.glPointSize(1.0f);
		perfil = this.aproxima(2, this.puntosControl
				.size(), nPasos);
		for (int i = 0; i < perfil.size(); i++) {
			PuntoVector p = perfil.get(i);
			gl.glBegin(GL.GL_POINTS);
			gl.glVertex2f((float) p.getX(), (float) p.getY());
			gl.glEnd();
		}
	}

	public void pintaConLineas(GL gl) {
		this.pintaPuntosControl(gl);
		gl.glPointSize(1.0f);
		perfil = this.aproxima(2, this.puntosControl
				.size(), nPasos);
		if (perfil.size() > 0) {
			PuntoVector p = perfil.get(0);
			for (int i = 1; i < perfil.size(); i++) {
				PuntoVector p2 = perfil.get(i);
				gl.glBegin(GL.GL_LINES);
				gl.glVertex2f((float) p.getX(), (float) p.getY());
				gl.glVertex2f((float) p2.getX(), (float) p2.getY());
				gl.glEnd();
				p = p2;
			}
		}
	}

	public void pintaPuntosControl(GL gl) {
		gl.glPointSize(3.0f);
		for (int i = 0; i < this.puntosControl.size(); i++) {
			PuntoVector p = this.puntosControl.get(i);
			gl.glBegin(GL.GL_POINTS);
			gl.glVertex2f((float) p.getX(), (float) p.getY());
			gl.glEnd();
		}
	}

	public void refina() {
		ArrayList<PuntoVector> puntosQ = new ArrayList<PuntoVector>();
		puntosQ.add(this.puntosControl.get(0));
		for (int i = 1; i < this.puntosControl.size() - 2; i++) {
			PuntoVector p1 = this.puntosControl.get(i - 1);
			PuntoVector p2 = this.puntosControl.get(i);
			PuntoVector p3 = this.puntosControl.get(i + 1);
			PuntoVector p4 = this.puntosControl.get(i + 2);
			PuntoVector q = new PuntoVector();
			p1.escalacion(0.25f);
			p2.escalacion(0.75f);
			p3.escalacion(0.75f);
			p4.escalacion(0.25f);
			q.setX(-p1.getX() + p2.getX() + p3.getX() + p4.getX());
			q.setY(-p1.getY() + p2.getY() + p3.getY() + p4.getY());
			q.setZ(-p1.getZ() + p2.getZ() + p3.getZ() + p4.getZ());
			puntosQ.add(q);
		}
		puntosQ.add(this.puntosControl.get(this.puntosControl.size() - 2));
		puntosQ.add(this.puntosControl.get(this.puntosControl.size() - 1));
		this.puntosControl = puntosQ;
	}

	public ArrayList<PuntoVector> getPerfil() {
		return perfil;
	}

	public void setPerfil(ArrayList<PuntoVector> perfil) {
		this.perfil = perfil;
	}
	
	public ArrayList<PuntoVector> getPuntosControl() {
		return puntosControl;
	}

	public void setPuntosControl(ArrayList<PuntoVector> puntosControl) {
		this.puntosControl = puntosControl;
	}

	public int getNPasos() {
		return nPasos;
	}

	public void setNPasos(int pasos) {
		nPasos = pasos;
	}

}
